<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>JS AOP编程</title>
	<script type="text/javascript">
		/*
		//  aop({options});
		//  By: adamchow2326@yahoo.com.au
		//  Version: 1.0
		//  Simple aspect oriented programming module
		//  support Aspect before, after and around
		//  usage:
				aop({
					context: myObject,      // scope context of the target function.
					target: "test",         // target function name
					before: function() {    // before function will be run before the target function
						console.log("aop before");
					},
					after: function() {     // after function will be run after the target function
						console.log("aop after");
					},
					around: function() {    // around function will be run before and after the target function
						console.log("aop around");
					}
				});
		*/
		var aop = (function() {
			var options = {},
				context = window,
				oFn,
				oFnArg,
				targetFn,
				targetFnSelector,
				beforeFn,
				afterFn,
				aroundFn,
				cloneFn = function(Fn) {
					if (typeof Fn === "function") {
						return eval('[' +Fn.toString()+ ']')[0];
					}
					return null;
				},
				checkContext = function() {
					if (options.context) {
						context = options.context;
					}
					if (typeof context[(options.target).name] === "function") {
						targetFnSelector = (options.target).name;
						targetFn = context[targetFnSelector];
					} 
					else if (typeof context[options.target] === "function") {
						targetFnSelector = options.target;
						targetFn = context[targetFnSelector];
					}
					if (targetFn) {
						oFn = cloneFn(targetFn);
						oFnArg = new Array(targetFn.length);
						return true;
					} 
					else {
						return false;
					}
				},
				run = function() {
					context[targetFnSelector] = function(oFnArg) {
						if (aroundFn){
							aroundFn.apply(this, arguments);
						}
						if (beforeFn){
							beforeFn.apply(this, arguments); // 'this' is context
						}
						oFn.apply(this, arguments);
						if (afterFn){
							afterFn.apply(this, arguments); // 'this' is context
						}
						if (aroundFn){
							aroundFn.apply(this, arguments);
						}
					};
				};
			
			return function(opt){
				if (opt && typeof opt === "object" && !opt.length) {
					options = opt;
					if (options.target && checkContext()) {
						if (options.before && typeof options.before === "function") {
							beforeFn = options.before;
						}
						if (options.after && typeof options.after === "function") {
							afterFn = options.after;
						}
						if (options.around && typeof options.after === "function") {
							aroundFn = options.around;
						}
						run();
					}
				}
			};
		})();
		
		// test examples
		// ----------------- aop modify global function ---------------//
		function test(name, age) {
			console.log("test fn. name = " + name + " age: " + age);
		}
		
		aop({
			target: "test",
			before: function() {
				console.log("aop before");
			},
			after: function() {
				console.log("aop after");
			},
			around: function() {
				console.log("aop around");
			}
		});
		// run
		test("adam", 6);
		
		
		// ----------------- aop test modify method in an object ---------------//
		var myobj = {
			myName: "testName",
			sayName: function() {
				console.log(this.myName);
			},
			childObj: {
				age: 6,
				say: function() {
					console.log(this.age);
				}
			}
		};
		
		aop({
			context: myobj,
			target: "sayName",
			before: function() {
				console.log("aop before say name = " + this.myName);
			},
			after: function() {
				console.log("aop after say name = " + this.myName);
			},
			around: function() {
				console.log("aop around say name = " + this.myName);
			}
		});
		// run
		myobj.sayName();
		
		
		aop({
			context: myobj.childObj,
			target: "say",
			before: function() {
				console.log("aop before say name = " + this.age);
			},
			after: function() {
				console.log("aop after say name = " + this.age);
			},
			around: function() {
				console.log("aop around say name = " + this.age);
			}
		});
		myobj.childObj.say();
	</script>
</head>
<body>
	
</body>
</html>